﻿/**
 * FFXIIRCGateway
 *
 * @package ffxiircgateway
 * @version $Id: TextCommand.cs 18 2009-03-17 05:13:49Z ayunyan $
 * @author ayunyan <ayu@commun.jp>
 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 * @link http://code.google.com/p/ffxiircgateway
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace FFXIIRCGateway
{
    public class TextCommand
    {
        private Process hProcess;
        private Memory memory;
        private int TextCommandFunc = -1;

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint dwFreeType);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, UIntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        private static extern Int32 WaitForSingleObject(IntPtr hObject, Int32 milliseconds);
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        private static extern Int32 CloseHandle(IntPtr hObject);

        private const int MEM_COMMIT = 0x1000;
        private const int MEM_RELEASE = 0x8000;
        private const int INFINITE = 0xffffff;
        private const int PAGE_READWRITE = 0x04;

        public TextCommand(Memory memory)
        {
            this.memory = memory;
            this.hProcess = this.memory.getProcess();

            this.TextCommandFunc = this.memory.searchPattern("..-..-..-..-..-..-..-..-..-..-..-..-8B-8C-24-08-04-00-00-55-57-8D-84-24-0C-02-00-00-68-00-02-00-00-50-33-ED-51-8B-0D-..-..-..-..-..-..-..-..");
        }

        public bool Send(string command)
        {
            if (this.TextCommandFunc == -1) return false;

            // 引数用のメモリを確保
            IntPtr pData = VirtualAllocEx(this.hProcess.Handle, (IntPtr)null, (uint)256, MEM_COMMIT, PAGE_READWRITE);
            if (pData == null)
            {
                return false;
            }

            IntPtr bytesOut;
            byte[] bytes = Encoding.Default.GetBytes(command);
            // 256byte以上の場合は省略
            if (bytes.Length > 255) Array.Resize(ref bytes, 255);
            // TextCommandの引数として渡すコマンドをメモリに書き込み
            Memory.WriteProcessMemory(this.hProcess.Handle, pData, bytes, (IntPtr)(bytes.Length + 1), out bytesOut);

            // TextCommandを起動
            IntPtr hRemoteThread = CreateRemoteThread(this.hProcess.Handle, (IntPtr)null, 0, (UIntPtr)this.TextCommandFunc, (IntPtr)pData, 0, out bytesOut);
            if (hRemoteThread == null)
            {
                // メモリを解放
                VirtualFreeEx(this.hProcess.Handle, pData, (UIntPtr)0, MEM_RELEASE);
                return false;
            }
            // TextCommand 終了まで待機
            WaitForSingleObject(hRemoteThread, INFINITE);
            CloseHandle(hRemoteThread);

            // メモリを解放
            VirtualFreeEx(this.hProcess.Handle, pData, (UIntPtr)0,  MEM_RELEASE);

            return true;
        }
    }
}
